//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.RegistryManagerModule
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Globalization;
    using System.Windows.Input;
    using Microsoft.Research.ScientificWorkflow.TridentComposer;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Practices.Unity;
    using Microsoft.Research.DataLayer;
    using Microsoft.Research.eResearch.Common.Eventing;
    using Microsoft.Research.eResearch.Common.Linq;
    using Microsoft.Win32;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
    using System.Configuration;
    using System.IO;
    using System.Text;
    using System.IO.Packaging;
    using System.Net;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Properties;
    using System.Drawing;
    using System.Xml;
    using System.Net.Mime;
    using System.Windows;
    using System.Collections;
    using System.Windows.Controls;
    using Microsoft.Research.ScientificWorkflow.UIDesigner;

    /// <summary>
    /// Presentation logic for displaying detailed information of 
    /// package and exporting them to a file.
    /// </summary>
    public class PackagingCenterPanePresenter : INotifyPropertyChanged, IDisposable
    {
        #region Private fields

        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Gets the instance of the workflow composer.
        /// </summary>
        private WorkflowComposer workflowComposer;

        /// <summary>
        /// This is the instance of current workflow model.
        /// </summary>
        private TridentWorkflowModel currentWorkflowModel;

        /// <summary>
        /// This give the current actities base model.
        /// </summary>
        private BaseModel actModel;

        /// <summary>
        /// Workflow name.
        /// </summary>
        private string workflowName;

        /// <summary>
        /// Default package file name
        /// </summary>
        private string workflowFileName;

        /// <summary>
        /// 
        /// </summary>
        private WorkflowCatalogViewModel currentWorkflowCatalogViewModel;

        /// <summary>
        /// A control that indicates that there is some work happening in the background.
        /// </summary>
        private WaitControl waitControl;

        /// <summary>
        /// Error code for invalid user.
        /// </summary>
        private const int INVALID_USER = 1000;

        /// <summary>
        /// Error code for upload failed.
        /// </summary>
        private const int UPLOAD_FAILED = 1001;

        #endregion

        #region Events

        /// <summary>
        /// Fired when the selected activity changes.
        /// </summary>
        public event EventHandler<EventArgs> Canceled;

        #endregion

        #region Public properties

        /// <summary>
        /// Gets or sets the PackagingCentralPaneView.
        /// </summary>
        /// <value>PackagingCentralPaneView.</value>
        public PackagingCenterPaneView View { get; private set; }

        /// <summary>
        /// Gets or sets the workflow assemblies collection.
        /// </summary>
        /// <value>The workflow assemblies collection.</value>
        public ObservableCollection<object> WorkflowAssembliesCollection { get; private set; }

        /// <summary>
        /// Gets or sets the required parameter collection.
        /// </summary>
        /// <value>The required parameter collection.</value>
        public ObservableCollection<object> RequiredParameterCollection { get; private set; }

        /// <summary>
        /// Gets or sets the workflow model.
        /// </summary>
        /// <value>The workflow model.</value>
        public TridentWorkflowModel WorkflowModel
        {
            get
            {
                return this.currentWorkflowModel;
            }
            set
            {

                this.currentWorkflowModel = value;

                if (this.currentWorkflowModel != null)
                {
                    this.ActModel = this.currentWorkflowModel.Root;
                    this.WorkflowName = this.currentWorkflowModel.Name;
                    this.WorkflowFileName = this.currentWorkflowModel.Name;
                }

                this.OnNotifyPropertyChanged("WorkflowModel");
            }
        }

        /// <summary>
        /// Gets or sets the name of the workflow.
        /// </summary>
        /// <value>The name of the workflow.</value>
        public string WorkflowName
        {
            get
            {
                return this.workflowName;

            }
            set
            {
                this.workflowName = value;
                this.OnNotifyPropertyChanged("WorkflowName");
            }
        }

        /// <summary>
        /// Gets or sets the name of the package.
        /// </summary>
        /// <value>The name of the package.</value>
        public string WorkflowFileName
        {
            get
            {
                return this.workflowFileName;
            }
            set
            {
                this.workflowFileName = value;
                this.OnNotifyPropertyChanged("WorkflowFileName");
            }
        }

        /// <summary>
        /// Gets or sets the activity model.
        /// </summary>
        /// <value>The act model.</value>
        public BaseModel ActModel
        {
            get
            {
                return this.actModel;
            }
            set
            {
                this.actModel = value;
                this.UpdateTreeView();
            }
        }

        /// <summary>
        /// Gets or sets the command to generate the package.
        /// </summary>
        public ICommand GeneratePackage { get; private set; }

        /// <summary>
        /// Gets or sets the command to upload the package.
        /// </summary>
        public ICommand UploadPackage { get; private set; }

        /// <summary>
        /// Gets or sets the command to cancel the operation.
        /// </summary>
        public ICommand Cancel { get; private set; }

        /// <summary>
        /// Gets or sets the command to get the information of the package.
        /// </summary>
        /// <value>The get data.</value>
        public ICommand GetData { get; private set; }

        /// <summary>
        /// Inidicates whether data is not present.
        /// </summary>
        private bool doesNotContainRequiredParameters;

        /// <summary>
        /// Gets whether data is not present.
        /// </summary>
        public bool DoesNotContainRequiredParameters
        {
            get
            {
                return this.doesNotContainRequiredParameters;
            }
            private set
            {
                this.doesNotContainRequiredParameters = value;
                this.OnNotifyPropertyChanged("DoesNotContainRequiredParameters");
            }
        }

        /// <summary>
        /// Inidicates whether data is present.
        /// </summary>
        private bool containRequiredParameters;

        /// <summary>
        /// Gets whether data is present.
        /// </summary>
        public bool ContainRequiredParameters
        {
            get
            {
                return this.containRequiredParameters;
            }
            private set
            {
                this.containRequiredParameters = value;
                this.DoesNotContainRequiredParameters = !this.ContainRequiredParameters;
                this.OnNotifyPropertyChanged("ContainRequiredParameters");
            }
        }

        /// <summary>
        /// Inidicates whether data is not present.
        /// </summary>
        private bool doesNotContainAssemblies;

        /// <summary>
        /// Gets whether data is not present.
        /// </summary>
        public bool DoesNotContainAssemblies
        {
            get
            {
                return this.doesNotContainAssemblies;
            }
            private set
            {
                this.doesNotContainAssemblies = value;
                this.OnNotifyPropertyChanged("DoesNotContainAssemblies");
            }
        }

        /// <summary>
        /// Inidicates whether data is present.
        /// </summary>
        private bool containAssemblies;

        /// <summary>
        /// Gets whether data is present.
        /// </summary>
        public bool ContainAssemblies
        {
            get
            {
                return this.containAssemblies;
            }
            private set
            {
                this.containAssemblies = value;
                this.DoesNotContainAssemblies = !this.ContainAssemblies;
                this.OnNotifyPropertyChanged("ContainAssemblies");
            }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="PackagingCentralPanePresenter"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="view">The view.</param>
        /// <param name="workflowComposer">The workflow composer.</param>
        /// <param name="workflowID">The workflow ID.</param>
        public PackagingCenterPanePresenter(
            IUnityContainer container,
            PackagingCenterPaneView view,
            WorkflowComposer workflowComposer,
            WorkflowCatalogViewModel workflowCatalogViewModel)
        {
            this.container = container;

            this.View = view;

            this.currentWorkflowCatalogViewModel = workflowCatalogViewModel;

            this.workflowComposer = workflowComposer;

            this.InitializeCommands();

            this.WorkflowAssembliesCollection = new ObservableCollection<object>();

            this.RequiredParameterCollection = new ObservableCollection<object>();

            this.View.Presenter = this;
        }

        #region Pubic methods

        /// <summary>
        /// Called when [get data].
        /// </summary>
        public void OnGetData()
        {
            try
            {
                this.WorkflowModel = this.workflowComposer.GetWorkflowModel(this.currentWorkflowCatalogViewModel.Id, false);

                this.ActModel = this.currentWorkflowModel.Root;

                this.GetWorkflowLibraries();

                this.GetWorkflowReqParameters();
            }
            catch (TridentCustomException tridentException)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(tridentException.Message);
            }
            catch (Exception exp)
            {
                TridentErrorHandler.HandleUIException(exp);
                TridentMessageBox.ShowTridentErrorMessageBox(exp.Message);
            }
        }

        /// <summary>
        /// Creates a temp package and uploads it.
        /// </summary>
        public void OnUploadPackage()
        {
            Application.Current.MainWindow.Cursor = Cursors.Wait;

            // Get the path of temporary package.
            string packageName = Guid.NewGuid().ToString() + ConfigurationManager.AppSettings["PackageFileNameExtension"];
            string tempPackagePath = Path.Combine(Path.GetTempPath(), packageName);
            OPCConnection opcConnection = null;

            // Get the top-most grid of the main window.
            Grid parentGrid = Application.Current.MainWindow.Content as Grid;
            try
            {
                // Generate an OPC Connection for the temp package.
                opcConnection = PackagingCenterPanePresenter.GenerateOPCConnection(tempPackagePath);

                // Export the workflow to this package.
                Activity workflow = this.workflowComposer.ExportWorkflow(this.currentWorkflowCatalogViewModel.Id, this.currentWorkflowModel, opcConnection);
                if (workflow != null)
                {
                    // Assign the workflow icon.
                    workflow.Icon = System.Drawing.Image.FromFile(this.View.WorkflowIcon);

                    // Get the package held by this connection.
                    Package package = OPCConnection.GetPackageReference();
                    if (package != null)
                    {
                        // Add the details that the user entered.
                        this.AddPackageDetails(package);

                        if (parentGrid != null)
                        {
                            // Create an instance of the wait control.
                            if (this.waitControl == null)
                            {
                                this.waitControl = new WaitControl(ManagementStudioResourceManager.GetString("Packaging"));
                                Grid.SetRowSpan(this.waitControl, 3);
                            }

                            // Disable all existing children in this grid.
                            foreach (UIElement element in parentGrid.Children)
                            {
                                element.IsEnabled = false;
                            }

                            // Add the wait control.
                            parentGrid.Children.Add(this.waitControl);
                        }
                        else
                        {
                            // If parent grid cannot be retrieved, add a wait cursor.
                            Application.Current.MainWindow.Cursor = Cursors.Wait;
                        }

                        // Add the parameters to be sent to background worker.
                        Hashtable param = new Hashtable();
                        param.Add("connection", opcConnection);
                        param.Add("workflow", workflow);
                        param.Add("package", package);
                        param.Add("tempPackagePath", tempPackagePath);
                        param.Add("accessibility", this.View.Accessibility);
                        param.Add("credential", new NetworkCredential(this.View.UserName, this.View.Password));

                        // Create a background worker to upload the package.
                        BackgroundWorker worker = new BackgroundWorker();
                        worker.DoWork += new DoWorkEventHandler(this.OnUploadPackageInBackground);
                        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.OnPackageUploaded);
                        worker.RunWorkerAsync(param);
                    }
                    else
                    {
                        // Set status to failure in status bar.
                        this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("PackageUploadFail"));
                    }
                }
                else
                {
                    // Set status to failure in status bar.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("PackageUploadFail"));
                }
            }
            catch (TridentCustomException tridentException)
            {
                // If an error occured and the wait control could not be removed, force remove it now.
                if (parentGrid != null && parentGrid.Children.Contains(this.waitControl))
                {
                    // Enable all child elements.
                    foreach (UIElement element in parentGrid.Children)
                    {
                        element.IsEnabled = true;
                    }

                    // Add the wait control.
                    parentGrid.Children.Remove(this.waitControl);
                }

                string message = string.Format(CultureInfo.CurrentCulture, ManagementStudioResourceManager.GetString("PackageUploadError"), tridentException.Message);
                TridentMessageBox.ShowTridentErrorMessageBox(message);
            }
            catch (Exception exp)
            {
                // If an error occured and the wait control could not be removed, force remove it now.
                if (parentGrid != null && parentGrid.Children.Contains(this.waitControl))
                {
                    // Enable all child elements.
                    foreach (UIElement element in parentGrid.Children)
                    {
                        element.IsEnabled = true;
                    }

                    // Add the wait control.
                    parentGrid.Children.Remove(this.waitControl);
                }

                TridentErrorHandler.HandleUIException(exp);
                string message = string.Format(CultureInfo.CurrentCulture, ManagementStudioResourceManager.GetString("PackageUploadError"), exp.Message);
                TridentMessageBox.ShowTridentErrorMessageBox(message);
            }
            finally
            {
                // Set the cursor back to arrow.
                Application.Current.MainWindow.Cursor = Cursors.Arrow;
            }
        }

        /// <summary>
        /// Called when [generate package].
        /// </summary>
        public void OnGeneratePackage()
        {
            this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);

            string packageFile = string.Empty;

            SaveFileDialog saveDialog = new SaveFileDialog();

            saveDialog.DefaultExt = ConfigurationManager.AppSettings["PackageFileNameExtension"];
            saveDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            saveDialog.Filter = "Workflow Package |*.twp";

            // Make sure the dialog accepts only Filename supported by Win32.
            saveDialog.ValidateNames = true;

            // Restores the previous Directory the user had selected to Open/Save the file.
            saveDialog.RestoreDirectory = true;

            // Asks the User to create a new file if not present in the Directory.
            saveDialog.OverwritePrompt = true;

            if (!string.IsNullOrEmpty(this.WorkflowFileName))
            {
                // Set the default file name to the on shown in the UI.
                saveDialog.FileName = Helper.GetSafeFileName(this.WorkflowFileName);
            }

            if ((bool)saveDialog.ShowDialog())
            {
                packageFile = saveDialog.FileName;
                Connection opcConnection = null;
                try
                {
                    System.Windows.Forms.Application.UseWaitCursor = true;

                    // If the file already exists, delete it.
                    if (File.Exists(packageFile))
                    {
                        File.Delete(packageFile);
                    }

                    opcConnection = GenerateOPCConnection(packageFile);
                    this.workflowComposer.ExportWorkflow(this.currentWorkflowCatalogViewModel.Id, this.currentWorkflowModel, opcConnection);
                    
                    // Add application version to the package.
                    Package opcPackage = OPCConnection.GetPackageReference();
                    PackagePart appVersionPart = opcPackage.CreatePart(new Uri("/Trident/AppVersion.xml", UriKind.Relative), "trident/tempobj");
                    string version = string.Format(CultureInfo.CurrentCulture, "<Version>{0}</Version>", PackagingCenterPanePresenter.GetBuildNumberFromRegistry());
                    byte[] bytes = Encoding.UTF7.GetBytes(version);
                    using (Stream stream = appVersionPart.GetStream())
                    {
                        stream.Write(bytes, 0, bytes.Length);
                    }

                    this.container.Resolve<IStatusMessageControl>().SetText(
                        ManagementStudioResourceManager.GetString("PackageCreatedSuccessfully"));
                }
                catch (TridentCustomException tridentException)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(tridentException.Message);
                }
                catch (Exception exp)
                {
                    TridentErrorHandler.HandleUIException(exp);
                    TridentMessageBox.ShowTridentErrorMessageBox(exp.Message);
                }
                finally
                {
                    if (opcConnection != null && opcConnection.Alive)
                    {
                        opcConnection.Close();
                        opcConnection = null;
                    }

                    System.Windows.Forms.Application.UseWaitCursor = false;
                }
            }
        }

        /// <summary>
        /// This method creates the Rest based Xml request
        /// and then requests an upload on the my experiment server.
        /// </summary>
        /// <param name="tridentPackage">
        /// Package to upload.
        /// </param>
        /// <param name="workflow">
        /// Workflow contained in the package.
        /// </param>
        /// <param name="packageFilePath">
        /// Path of the package.
        /// </param>
        /// <param name="accessibility">
        /// Accessibility property for the package.
        /// </param>
        /// <param name="credential">
        /// User credentials.
        /// </param>
        public static void UploadWorkflow(Activity workflow, Package tridentPackage, string packageFilePath, string accessibility, ICredentials credential)
        {
            // Get the upload template.
            object uploadTemplate = Resources.ResourceManager.GetObject("UploadTemplate");
            if (uploadTemplate != null)
            {
                // Set the appropriate fields.
                StringBuilder uploadXml = new StringBuilder(uploadTemplate.ToString());
                tridentPackage.Close();

                // Add workflow title.
                if (!string.IsNullOrEmpty(workflow.Label))
                {
                    uploadXml.Replace("%%TITLE%%", workflow.Label);
                }
                else if (!string.IsNullOrEmpty(workflow.Name))
                {
                    uploadXml.Replace("%%TITLE%%", workflow.Name);
                }
                else
                {
                    uploadXml.Replace("%%TITLE%%", "Untitled");
                }

                // Add workflow description.
                uploadXml.Replace("%%DESCRIPTION%%", workflow.Description);

                // Add workflow accessibility.
                string accessString = "";
                if (accessibility.Equals("Private", StringComparison.OrdinalIgnoreCase))
                {
                    accessString = "<permissions/>";
                }
                else if (accessibility.Equals("Public", StringComparison.OrdinalIgnoreCase))
                {
                    accessString = "<permissions><permission><category>public</category><privilege type=\"view\"/></permission></permissions>";
                }
                else if (accessibility.Equals("Download", StringComparison.OrdinalIgnoreCase))
                {
                    accessString = "<permissions><permission><category>public</category><privilege type=\"view\"/><privilege type=\"download\"/></permission></permissions>";
                }

                uploadXml.Replace("%%PERMISSION%%", accessString);

                // Add workflow icon.
                using (MemoryStream stream = new MemoryStream())
                {
                    workflow.Icon.Save(stream, workflow.Icon.RawFormat);
                    uploadXml.Replace("%%PREVIEWIMAGE%%", Convert.ToBase64String(stream.ToArray()));
                }

                // Add workflow package contents.
                byte[] packageContents = File.ReadAllBytes(packageFilePath);
                uploadXml.Replace("%%PACKAGECONTENTS%%", Convert.ToBase64String(packageContents));

                // Post the XML to MyExperiment site.
                HttpWebRequest request = WebRequest.Create(ConfigurationManager.AppSettings["MyExperimentUploadService"]) as HttpWebRequest;
                request.Method = "POST";
                request.Credentials = credential;

                byte[] data = UTF8Encoding.UTF8.GetBytes(uploadXml.ToString());
                request.ContentLength = data.Length;
                request.Timeout = int.MaxValue;
                request.MaximumResponseHeadersLength = int.MaxValue;

                // Write data.
                using (Stream postStream = request.GetRequestStream())
                {
                    postStream.Write(data, 0, data.Length);
                }

                // Get response.
                using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                {
                    // Try to get a response. If there is a response, then it's a valid post.
                }
            }
        }

        /// <summary>
        /// Called when [cancel].
        /// </summary>
        public void OnCancel()
        {
            this.Canceled.Fire(this, new EventArgs());
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Retrieves the current build number from windows registry.
        /// </summary>
        /// <returns>Build Number</returns>
        private static string GetBuildNumberFromRegistry()
        {
            string buildNumber = string.Empty;
            try
            {
                RegistryKey regKey = Registry.LocalMachine.OpenSubKey(Constants.PRODUCT_VERSION_REGISTRY_PATH);
                if (null != regKey)
                {
                    buildNumber = regKey.GetValue(Constants.PRODUCT_VERSION_REGISTRY_KEY).ToString();
                }
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleUIException(exception);
            }

            return buildNumber;
        }

        /// <summary>
        /// Initialize the commands.
        /// </summary>
        private void InitializeCommands()
        {
            this.GetData = new DelegateCommand<object>(p => { this.OnGetData(); });

            this.GeneratePackage = new DelegateCommand<object>(p => { this.OnGeneratePackage(); });

            this.UploadPackage = new DelegateCommand<object>(p => { this.OnUploadPackage(); });

            this.Cancel = new DelegateCommand<object>(p => { this.OnCancel(); });
        }

        /// <summary>
        /// Gets the workflow req parameters.
        /// </summary>
        private void GetWorkflowReqParameters()
        {
            try
            {
                this.currentWorkflowCatalogViewModel.ParameterList
                    .ForEach(param => this.RequiredParameterCollection.Add(
                        new { Name = param.Name, Value = param.Value, DataType = param.DataType }));

                this.ContainRequiredParameters = this.RequiredParameterCollection.Count > 0;
            }
            catch (Exception exp)
            {
                TridentErrorHandler.HandleUIException(exp);
            }
        }

        /// <summary>
        /// Gets the workflow libraries.
        /// </summary>
        private void GetWorkflowLibraries()
        {
            try
            {
                this.currentWorkflowCatalogViewModel.LibraryList
                    .ForEach(model => this.WorkflowAssembliesCollection.Add(new { Name = model.Name }));

                this.ContainAssemblies = this.WorkflowAssembliesCollection.Count > 0;
            }
            catch (Exception exp)
            {
                TridentErrorHandler.HandleUIException(exp);
            }
        }

        /// <summary>
        /// Generates the OPC connection.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>OPC connection.</returns>
        private static OPCConnection GenerateOPCConnection(string fileName)
        {
            OPCConnection opcConnection = null;
            try
            {
                IConnMgrInfoParams opcParam = OPCConnectionParameters.CreateConnection("testName", fileName);
                ConnectionManager connMgr = ConnectionManager.Create(ConnectionManager.CancelBehavior.ThrowException);
                opcConnection = connMgr.OpenConnection(opcParam) as OPCConnection;
            }
            catch (Exception exp)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                   exp,
                   TridentErrorConstants.ErrorNumber1000000045,
                   exp.Message);
            }

            return opcConnection;
        }

        /// <summary>
        /// Update the activity tree.
        /// </summary>
        private void UpdateTreeView()
        {
            this.View.ActivityTreeView.ItemsSource = new ObservableCollection<BaseModel>() { this.actModel };
        }

        /// <summary>
        /// Checks if the username and password entered are valid or not.
        /// </summary>
        /// <param name="credential">
        /// User credentials.
        /// </param>
        /// <returns>
        /// True if valid, False otherwise.
        /// </returns>
        private bool ValidateUser(NetworkCredential credential)
        {
            bool validUser = true;
            try
            {
                HttpWebRequest request = WebRequest.Create(ConfigurationManager.AppSettings["MyExperimentAuthenticateService"]) as HttpWebRequest;
                request.Method = "GET";
                request.Credentials = credential;
                using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                {
                    // Try to get a response. If there is a response, then it's a valid user.
                }
            }
            catch
            {
                // If there is any exception, mark it as invalid.
                validUser = false;
            }

            return validUser;
        }

        /// <summary>
        /// Adds the package details that the user entered.
        /// </summary>
        /// <param name="package">
        /// Package object.
        /// </param>
        private void AddPackageDetails(Package package)
        {
            string uriString = "/MyExp/WFData.xml";
            XmlDocument document = new XmlDocument();
            document.LoadXml("<WFDetails/>");

            XmlElement attribElement = document.CreateElement("Attributions");
            document.FirstChild.AppendChild(attribElement);

            foreach (string str in this.View.Attributions)
            {
                XmlElement attribChild = document.CreateElement("Attribution");
                attribChild.InnerText = str;
                attribElement.AppendChild(attribChild);
            }

            XmlElement creditsElement = document.CreateElement("Credits");
            document.FirstChild.AppendChild(creditsElement);
            foreach (string str in this.View.Credits)
            {
                XmlElement creditChild = document.CreateElement("Credit");
                creditChild.InnerText = str;
                creditsElement.AppendChild(creditChild);
            }

            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] data = encoding.GetBytes(document.OuterXml);
            PackagePart part = package.CreatePart(new Uri(uriString, UriKind.Relative), MediaTypeNames.Text.Plain);
            part.GetStream().Write(data, 0, data.Length);

            package.Flush();

            // Add V-card.
            this.AddCreatorVCard(package);

            // Add workflow icon.
            this.AddWorkflowIcon(package);
        }

        /// <summary>
        /// Adds V-Card details to the package.
        /// </summary>
        /// <param name="package">
        /// Package object.
        /// </param>
        private void AddCreatorVCard(Package package)
        {
            string fileName = Path.GetFileName(this.View.VCardFile);
            if (!string.IsNullOrEmpty(fileName))
            {
                string vCardUri = "/MyExp/" + fileName;
                vCardUri = Uri.EscapeUriString(vCardUri);
                PackagePart part = package.CreatePart(new Uri(vCardUri, UriKind.Relative), MediaTypeNames.Application.Octet);

                byte[] fileData = File.ReadAllBytes(fileName);
                part.GetStream().Write(fileData, 0, fileData.Length);
                package.Flush();
            }
        }

        /// <summary>
        /// Adds V-Card details to the package.
        /// </summary>
        /// <param name="package">
        /// Package object.
        /// </param>
        private void AddWorkflowIcon(Package package)
        {
            string fileName = Path.GetFileName(this.View.WorkflowIcon);
            if (string.IsNullOrEmpty(fileName))
            {
                string imageUri = "/MyExp/Image/" + fileName;
                imageUri = Uri.EscapeUriString(imageUri);
                PackagePart part = package.CreatePart(new Uri(imageUri, UriKind.Relative), MediaTypeNames.Image.Jpeg);

                byte[] fileData = File.ReadAllBytes(fileName);
                part.GetStream().Write(fileData, 0, fileData.Length);
                package.Flush();
            }
        }

        /// <summary>
        /// Occurs after the package has been uploaded.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnPackageUploaded(object sender, RunWorkerCompletedEventArgs e)
        {
            // Get the top-most grid of the main window.
            Grid parentGrid = Application.Current.MainWindow.Content as Grid;
            if (parentGrid != null)
            {
                // Enable all child elements.
                foreach (UIElement element in parentGrid.Children)
                {
                    element.IsEnabled = true;
                }

                // Add the wait control.
                parentGrid.Children.Remove(this.waitControl);
            }

            Hashtable param = e.Result as Hashtable;
            if (param != null)
            {
                // Set status to success in status bar.
                this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("PackageUploadSuccess"));

                OPCConnection opcConnection = param["connection"] as OPCConnection;
                string tempPackagePath = param["tempPackagePath"].ToString();

                // Delete the temp package.
                try
                {
                    File.Delete(tempPackagePath);
                }
                catch
                {
                    // Some error occured while deleting the temp package.
                    // Do nothing. Let it remain.
                }

                if (opcConnection != null && opcConnection.Alive)
                {
                    opcConnection.Close();
                    opcConnection = null;
                }
            }
            else if (e.Result != null)
            {
                int errorCode = -1;
                int.TryParse(e.Result.ToString(), out errorCode);

                if (errorCode == PackagingCenterPanePresenter.INVALID_USER)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(Application.Current.MainWindow, ManagementStudioResourceManager.GetString("MyExperimentAccountInvalid"));
                }
                else if (errorCode == PackagingCenterPanePresenter.UPLOAD_FAILED)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(Application.Current.MainWindow, ManagementStudioResourceManager.GetString("MyExperimentUploadError"));
                }
            }
        }

        /// <summary>
        /// Occurs when the background worker thread starts its execution.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnUploadPackageInBackground(object sender, DoWorkEventArgs e)
        {
            Hashtable param = e.Argument as Hashtable;
            if (param != null)
            {
                // Get all the required parameters.
                Activity workflow = param["workflow"] as Activity;
                Package package = param["package"] as Package;
                string tempPackagePath = param["tempPackagePath"].ToString();
                string accessibility = param["accessibility"].ToString();
                NetworkCredential credential = param["credential"] as NetworkCredential;

                // Validate user credentials.
                bool validUser = this.ValidateUser(credential);
                if (!validUser)
                {
                    e.Result = PackagingCenterPanePresenter.INVALID_USER;
                }
                else
                {
                    try
                    {
                        // Upload the workflow package.
                        PackagingCenterPanePresenter.UploadWorkflow(workflow, package, tempPackagePath, accessibility, credential);

                        // Store the param so that WorkerCompleted event can access it.
                        e.Result = param;
                    }
                    catch (WebException)
                    {
                        // Set error code.
                        e.Result = PackagingCenterPanePresenter.UPLOAD_FAILED;
                    }
                }
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Event for property change.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raise property changed eventargs.
        /// </summary>
        /// <param name="changeProperty">Property anme.</param>
        private void OnNotifyPropertyChanged(string changeProperty)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(changeProperty));
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Clears the collection and View Object
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// nullify and Disposes the object 
        /// </summary>
        /// <param name="disposing"></param>
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.container.Dispose();
                this.currentWorkflowModel = null;
                this.View = null;
            }
        }

        #endregion
    }
}
